﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MvvmEx.EventArgs;

namespace MvvmEx.Controls.DateShow
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作，然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中: 
    ///
    ///     xmlns:MyNamespace="clr-namespace:MvvmEx.Controls.DateShow"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中: 
    ///
    ///     xmlns:MyNamespace="clr-namespace:MvvmEx.Controls.DateShow;assembly=MvvmEx.Controls.DateShow"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用，
    /// 并重新生成以避免编译错误: 
    ///
    ///     在解决方案资源管理器中右击目标项目，然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:DateShow/>
    ///
    /// </summary>
    public class DateShow : Control
    {
        #region 字段

        private ItemsControl _itemsControl;
        private List<DateShowItem> _dateShowItems;
        private static readonly Dictionary<DayOfWeek, int> _dayOfWeek = new Dictionary<DayOfWeek, int>()
        {
            {DayOfWeek.Monday, 0},
            {DayOfWeek.Tuesday, 1},
            {DayOfWeek.Wednesday, 2},
            {DayOfWeek.Thursday, 3},
            {DayOfWeek.Friday, 4},
            {DayOfWeek.Saturday, 5},
            {DayOfWeek.Sunday, 6}
        };
        #endregion

        #region 依赖属性

        #region 数据源


        public ObservableCollection<DateTime> ItemSource
        {
            get { return (ObservableCollection<DateTime>)GetValue(ItemSourceProperty); }
            set { SetValue(ItemSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemSourceProperty =
            DependencyProperty.Register("ItemSource", typeof(ObservableCollection<DateTime>), typeof(DateShow), new PropertyMetadata(null, PropertyChanged));


        #endregion

        #region 日期

        public DateTime Date
        {
            get { return (DateTime)GetValue(DateProperty); }
            set { SetValue(DateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Date.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DateProperty =
            DependencyProperty.Register("Date", typeof(DateTime), typeof(DateShow), new PropertyMetadata(DateTime.MinValue,
                (s, e) =>
                {
                    var obj = s as DateShow;

                    if (obj == null) return;

                    var date = (DateTime)e.NewValue;

                    obj.Year = date.Year;
                    obj.Month = date.Month;
                }));

        #endregion

        #region 年
        public int Year
        {
            get { return (int)GetValue(YearProperty); }
            set { SetValue(YearProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Year.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YearProperty =
            DependencyProperty.Register("Year", typeof(int), typeof(DateShow), new PropertyMetadata(1970, PropertyChanged));
        #endregion

        #region 月

        public int Month
        {
            get { return (int)GetValue(MonthProperty); }
            set { SetValue(MonthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Month.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MonthProperty =
            DependencyProperty.Register("Month", typeof(int), typeof(DateShow), new PropertyMetadata(1, PropertyChanged));

        #endregion

        #endregion

        #region 命令

        /// <summary>
        /// 上一年
        /// </summary>
        public static readonly RoutedCommand PreYearCommand = new RoutedCommand("PreYear", typeof(DateShow));

        /// <summary>
        /// 下一年
        /// </summary>
        public static readonly RoutedCommand NextYearCommand = new RoutedCommand("NextYear", typeof(DateShow));

        /// <summary>
        /// 上一月
        /// </summary>
        public static readonly RoutedCommand PreMonthCommand = new RoutedCommand("PreMonthCommand", typeof(DateShow));

        /// <summary>
        /// 下一月
        /// </summary>
        public static readonly RoutedCommand NextMonthCommand = new RoutedCommand("NextMonthCommand", typeof(DateShow));



        #endregion

        #region 事件

        /// <summary>
        /// 选择日期改变
        /// </summary>
        public event EventHandler<RoutedEventArgs<DateTime>> SelectedDateChanged
        {
            add
            {
                this.AddHandler(SelectedDateChangedEvent, value);
            }
            remove
            {
                this.RemoveHandler(SelectedDateChangedEvent, value);
            }
        }

        public static readonly RoutedEvent SelectedDateChangedEvent =
            EventManager.RegisterRoutedEvent("SelectedDateChanged", RoutingStrategy.Bubble,
                typeof(EventHandler<RoutedEventArgs<DateTime>>), typeof(DateShow));



        /// <summary>
        /// 日期变更
        /// </summary>
        public event EventHandler<RoutedEventArgs<DateTime>> DateChanged
        {
            add
            {
                this.AddHandler(DateChangedEvent, value);
            }
            remove
            {
                this.RemoveHandler(DateChangedEvent, value);
            }
        }

        public static readonly RoutedEvent DateChangedEvent = EventManager.RegisterRoutedEvent("DateChanged",
            RoutingStrategy.Bubble, typeof(EventHandler<RoutedEventArgs<DateTime>>), typeof(DateShow));

        #endregion

        #region 构造
        static DateShow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DateShow), new FrameworkPropertyMetadata(typeof(DateShow)));

            InitCommands();
        }

        public DateShow()
        {
            _dateShowItems = new List<DateShowItem>();
        }
        #endregion

        #region 公有方法

        #region 日期变更
        protected virtual void OnDateChanged(DateTime e)
        {
            var eventArgs = new RoutedEventArgs<DateTime>(DateChangedEvent, this, e);

            this.RaiseEvent(eventArgs);
        }
        #endregion

        #region 选择日期
        public virtual void OnSelectedDateChanged(DateTime e)
        {
            var eventArgs = new RoutedEventArgs<DateTime>(SelectedDateChangedEvent, this, e);

            this.RaiseEvent(eventArgs);
        }
        #endregion

        #region 属性变更

        public static void PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var obj = sender as DateShow;

            obj?.Render();
        }

        #endregion

        #endregion

        #region 私有方法

        #region 注册事件

        private static void InitCommands()
        {
            var executeRoutedEventHandler = new ExecutedRoutedEventHandler(OnExecutedRoutedEventHandler);

            CommandManager.RegisterClassCommandBinding(typeof(DateShow), new CommandBinding(PreYearCommand, executeRoutedEventHandler));
            CommandManager.RegisterClassCommandBinding(typeof(DateShow), new CommandBinding(NextYearCommand, executeRoutedEventHandler));
            CommandManager.RegisterClassCommandBinding(typeof(DateShow), new CommandBinding(PreMonthCommand, executeRoutedEventHandler));
            CommandManager.RegisterClassCommandBinding(typeof(DateShow), new CommandBinding(NextMonthCommand, executeRoutedEventHandler));

        }

        #endregion

        #region 命令实现

        private static void OnExecutedRoutedEventHandler(object target, ExecutedRoutedEventArgs e)
        {
            var obj = target as DateShow;

            if (obj == null) return;

            if (e.Command == PreYearCommand)
            {
                obj.PreYearCommandHandler();
            }
            else if (e.Command == NextYearCommand)
            {
                obj.NextYearCommandHandler();
            }
            else if (e.Command == PreMonthCommand)
            {
                obj.PreMonthCommandHandler();
            }
            else if (e.Command == NextMonthCommand)
            {
                obj.NextMonthCommandHandler();
            }
        }

        #endregion

        #region 上一年命令

        public void PreYearCommandHandler()
        {
            Year--;

            OnDateChanged(new DateTime(Year, Month, 1));
        }

        #endregion

        #region 下一年命令

        public void NextYearCommandHandler()
        {
            Year++;

            OnDateChanged(new DateTime(Year, Month, 1));
        }

        #endregion

        #region 上一月命令

        public void PreMonthCommandHandler()
        {
            if (Month == 1)
            {
                Year--;
                Month = 12;
            }
            else
            {
                Month--;
            }

            OnDateChanged(new DateTime(Year, Month, 1));
        }

        #endregion

        #region 下一月命令

        public void NextMonthCommandHandler()
        {
            if (Month == 12)
            {
                Year++;
                Month = 1;
            }
            else
            {
                Month++;
            }

            OnDateChanged(new DateTime(Year, Month, 1));
        }

        #endregion

        #region 渲染

        private void Render()
        {
            CaculateCalendar();

            Mapping();
        }

        #endregion

        #region 匹配

        private void Mapping()
        {
            if (ItemSource == null) return;

            foreach (var dateTime in ItemSource)
            {
                var item = _dateShowItems.FirstOrDefault(x =>
                     x.Date.Year == dateTime.Year
                     &&
                     x.Date.Month == dateTime.Month
                     &&
                     x.Date.Day == dateTime.Day);

                if (item != null)
                {
                    item.IsSelected = true;
                    item.IsEnabled = true;
                }
            }
        }

        #endregion

        #region 计算日期

        private void CaculateCalendar()
        {
            _dateShowItems.Clear();

            //找到这个月有多少天
            var days = DateTime.DaysInMonth(Year, Month);

            for (int i = 1; i <= days; i++)
            {
                _dateShowItems.Add(new DateShowItem(this) { Date = new DateTime(Year, Month, i) });
            }

            //找出第一天
            var firstDay = _dateShowItems.First().Date;

            //找出1号是星期几
            var index = _dayOfWeek[firstDay.DayOfWeek];

            //补齐前面的日期
            for (int i = 1; i <= index; i++)
            {
                var newDate = firstDay.AddDays(-i);
                _dateShowItems.Insert(0, new DateShowItem(this) { Date = newDate, });
            }

            //找出最后一天
            var lastDay = _dateShowItems.Last().Date;

            index = _dayOfWeek[lastDay.DayOfWeek];

            //补齐后面的日期
            for (int i = 1; i < _dayOfWeek.Count - index; i++)
            {
                var newDate = lastDay.AddDays(i);
                _dateShowItems.Add(new DateShowItem(this) { Date = newDate, });
            }

            //找到今天，高亮显示
            var today = _dateShowItems.FirstOrDefault(x => x.Date.Year == DateTime.Now.Year && x.Date.Month == DateTime.Now.Month &&
                                                x.Date.Day == DateTime.Now.Day);

            if (today != null)
            {
                today.IsToDay = true;
            }

            //计算行列
            foreach (var item in _dateShowItems)
            {
                var itemIndex = _dateShowItems.IndexOf(item);

                var row = itemIndex / _dayOfWeek.Count;
                var column = itemIndex % _dayOfWeek.Count;

                item.Row = row;
                item.Column = column;
            }

            if (_itemsControl != null)
            {
                _itemsControl.Items.Clear();
                _dateShowItems.ForEach(x => _itemsControl.Items.Add(x));
            }
        }

        #endregion

        #endregion

        #region 重载

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _itemsControl = GetTemplateChild("PART_Items") as ItemsControl;

            if (_itemsControl != null)
            {
                _itemsControl.Items.Clear();

                //添加新的项目
                foreach (var item in _dateShowItems)
                {
                    _itemsControl.Items.Add(item);
                }
            }
        }

        #endregion



    }
}
